├── web_service ├── __init__.py ├── main.py └── routes.py ├── .gitignore ├── assets ├── img │ ├── info.png │ ├── zoom.png │ ├── annotations.png │ ├── CDSA_Slide_50.png │ ├── drawicons │ │ ├── circle.png │ │ ├── square.png │ │ ├── Pin1_Blue.png │ │ ├── Pin1_Green.png │ │ ├── Pin1_Red.png │ │ ├── rectangle.png │ │ └── polyline_v1.png │ └── openseadragon │ │ ├── sort_asc.png │ │ ├── home_hover.png │ │ ├── home_rest.png │ │ ├── next_hover.png │ │ ├── next_rest.png │ │ ├── sort_both.png │ │ ├── sort_desc.png │ │ ├── zoomin_rest.png │ │ ├── back_disabled.png │ │ ├── back_enabled.png │ │ ├── fullpage_rest.png │ │ ├── home_pressed.png │ │ ├── next_pressed.png │ │ ├── previous_rest.png │ │ ├── zoomin_hover.png │ │ ├── zoomout_hover.png │ │ ├── zoomout_rest.png │ │ ├── forward_disabled.png │ │ ├── forward_enabled.png │ │ ├── fullpage_hover.png │ │ ├── fullpage_pressed.png │ │ ├── home_grouphover.png │ │ ├── next_grouphover.png │ │ ├── previous_hover.png │ │ ├── previous_pressed.png │ │ ├── zoomin_pressed.png │ │ ├── zoomout_pressed.png │ │ ├── back_enabled_hover.png │ │ ├── sort_asc_disabled.png │ │ ├── sort_desc_disabled.png │ │ ├── zoomin_grouphover.png │ │ ├── zoomout_grouphover.png │ │ ├── forward_enabled_hover.png │ │ ├── fullpage_grouphover.png │ │ ├── glyphicons-halflings.png │ │ ├── previous_grouphover.png │ │ └── glyphicons-halflings-white.png ├── js │ ├── dsa.js │ ├── colornames_to_hex_hash.js │ ├── mousetrap.min.js │ ├── annotationState_control_functions.js │ ├── bootstrap-switch.js │ ├── openseadragon_setup_functions.js │ ├── viewer.js │ └── AperioAnnotationLib.js ├── libs │ ├── openseadragon-viewerinputhook.min.js │ ├── openseadragon-imaginghelper.min.js │ ├── bootstrap-colorpicker-module.js │ └── openseadragon-scalebar.js └── css │ ├── digitalslidearchive.css │ ├── bootstrap-switch.css │ └── colorpicker.css ├── app ├── app.module.js ├── app.constant.js └── components │ ├── markup │ ├── service.js │ └── controller.js │ ├── layer │ ├── service.js │ └── controller.js │ ├── image │ └── controller.js │ └── aperio │ └── controller.js ├── README.md └── LICENSE /web_service/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | nohup.out 3 | -------------------------------------------------------------------------------- /assets/img/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/info.png -------------------------------------------------------------------------------- /assets/img/zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/zoom.png -------------------------------------------------------------------------------- /app/app.module.js: -------------------------------------------------------------------------------- 1 | //Create a module 2 | //Add color picker dependency 3 | var app = angular.module('dsa', ['colorpicker.module']); 4 | -------------------------------------------------------------------------------- /assets/img/annotations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/annotations.png -------------------------------------------------------------------------------- /assets/img/CDSA_Slide_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/CDSA_Slide_50.png -------------------------------------------------------------------------------- /assets/img/drawicons/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/drawicons/circle.png -------------------------------------------------------------------------------- /assets/img/drawicons/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/drawicons/square.png -------------------------------------------------------------------------------- /assets/img/drawicons/Pin1_Blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/drawicons/Pin1_Blue.png -------------------------------------------------------------------------------- /assets/img/drawicons/Pin1_Green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/drawicons/Pin1_Green.png -------------------------------------------------------------------------------- /assets/img/drawicons/Pin1_Red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/drawicons/Pin1_Red.png -------------------------------------------------------------------------------- /assets/img/drawicons/rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/drawicons/rectangle.png -------------------------------------------------------------------------------- /assets/img/drawicons/polyline_v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/drawicons/polyline_v1.png -------------------------------------------------------------------------------- /assets/img/openseadragon/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/sort_asc.png -------------------------------------------------------------------------------- /assets/img/openseadragon/home_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/home_hover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/home_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/home_rest.png -------------------------------------------------------------------------------- /assets/img/openseadragon/next_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/next_hover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/next_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/next_rest.png -------------------------------------------------------------------------------- /assets/img/openseadragon/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/sort_both.png -------------------------------------------------------------------------------- /assets/img/openseadragon/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/sort_desc.png -------------------------------------------------------------------------------- /assets/img/openseadragon/zoomin_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/zoomin_rest.png -------------------------------------------------------------------------------- /assets/img/openseadragon/back_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/back_disabled.png -------------------------------------------------------------------------------- /assets/img/openseadragon/back_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/back_enabled.png -------------------------------------------------------------------------------- /assets/img/openseadragon/fullpage_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/fullpage_rest.png -------------------------------------------------------------------------------- /assets/img/openseadragon/home_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/home_pressed.png -------------------------------------------------------------------------------- /assets/img/openseadragon/next_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/next_pressed.png -------------------------------------------------------------------------------- /assets/img/openseadragon/previous_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/previous_rest.png -------------------------------------------------------------------------------- /assets/img/openseadragon/zoomin_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/zoomin_hover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/zoomout_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/zoomout_hover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/zoomout_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/zoomout_rest.png -------------------------------------------------------------------------------- /assets/img/openseadragon/forward_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/forward_disabled.png -------------------------------------------------------------------------------- /assets/img/openseadragon/forward_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/forward_enabled.png -------------------------------------------------------------------------------- /assets/img/openseadragon/fullpage_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/fullpage_hover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/fullpage_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/fullpage_pressed.png -------------------------------------------------------------------------------- /assets/img/openseadragon/home_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/home_grouphover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/next_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/next_grouphover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/previous_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/previous_hover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/previous_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/previous_pressed.png -------------------------------------------------------------------------------- /assets/img/openseadragon/zoomin_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/zoomin_pressed.png -------------------------------------------------------------------------------- /assets/img/openseadragon/zoomout_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/zoomout_pressed.png -------------------------------------------------------------------------------- /app/app.constant.js: -------------------------------------------------------------------------------- 1 | //Define constant values that can be used throughout AngularJS 2 | app.constant('api', 3 | { 4 | url: 'http://localhost', 5 | port: 5003 6 | }); 7 | 8 | -------------------------------------------------------------------------------- /assets/img/openseadragon/back_enabled_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/back_enabled_hover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/sort_asc_disabled.png -------------------------------------------------------------------------------- /assets/img/openseadragon/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/sort_desc_disabled.png -------------------------------------------------------------------------------- /assets/img/openseadragon/zoomin_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/zoomin_grouphover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/zoomout_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/zoomout_grouphover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/forward_enabled_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/forward_enabled_hover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/fullpage_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/fullpage_grouphover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/glyphicons-halflings.png -------------------------------------------------------------------------------- /assets/img/openseadragon/previous_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/previous_grouphover.png -------------------------------------------------------------------------------- /assets/img/openseadragon/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitalSlideArchive-Legacy/openseadragon-annotations/HEAD/assets/img/openseadragon/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /web_service/main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask.ext.cors import CORS 3 | from routes import dsa 4 | 5 | app = Flask('dsa') 6 | app.register_blueprint(dsa) 7 | 8 | cors = CORS(app, resources={r"/*": {"origins": "*"}}) 9 | 10 | app.run(host='0.0.0.0', port=5003, debug=True) 11 | -------------------------------------------------------------------------------- /app/components/markup/service.js: -------------------------------------------------------------------------------- 1 | //Define a service for the markup 2 | //Add root scope dependency 3 | app.factory("markupService", function($window){ 4 | 5 | getAnnotationIndex = function(index){ 6 | var ants = $window.annotationState.annotations; 7 | 8 | for(var i=0;i 0: 38 | data = data[0] 39 | 40 | for layer_id in data['layers']: 41 | ant = db.annotations.find_one({"_id": layer_id}, {"_id": 0}) 42 | layers.append(ant) 43 | 44 | return jsonify({"status": "success", "code": 200, "layers": layers}) 45 | 46 | return jsonify({"status": "success", "code": 404}) 47 | -------------------------------------------------------------------------------- /app/components/markup/controller.js: -------------------------------------------------------------------------------- 1 | //Define a controller for markups 2 | //Add window dependency 3 | app.controller("markupCtrl", function($scope, $window, markupService){ 4 | 5 | //Define scope variables 6 | $scope.layers = []; 7 | $scope.activeLayerIndex = 0; 8 | $scope.index = 0; 9 | 10 | //Listen to changes for variable layers broadcasted 11 | //by the layer controller and update the $scope layer 12 | //variable 13 | $scope.$on('layers', function(events, args){ 14 | $scope.layers = args.layers; 15 | $scope.activeLayerIndex = args.activeLayerIndex; 16 | }); 17 | 18 | /** 19 | * Add new markup 20 | * Add new markup to the list. This method takes in all markups to 21 | * update the scope variable and then update the scope index 22 | * @param {Object} markups 23 | */ 24 | $scope.add = function(markup){ 25 | $scope.layers[$scope.activeLayerIndex].markups[markup.data.index] = markup; 26 | var obj = {layers: $scope.layers, activeLayerIndex: $scope.activeLayerIndex}; 27 | $scope.$emit('layers', obj); 28 | }; 29 | 30 | /** 31 | * Update markup 32 | * @param {Number} index 33 | * @param {Object} markup 34 | */ 35 | $scope.update = function(markup){ 36 | markup.element.style.borderColor = markup.data.color; 37 | markup.data.color = markup.data.color; 38 | markup.data.name = markup.data.name; 39 | }; 40 | 41 | /** 42 | * Remove markup 43 | * @param {Number} index 44 | */ 45 | $scope.remove = function(index){ 46 | var antIndex = markupService.getAnnotationIndex(index); 47 | $window.annotationState.annotations[antIndex].detach(); 48 | $window.annotationState.annotations.splice(antIndex, 1); 49 | delete $scope.layers[$scope.activeLayerIndex].markups[index]; 50 | var obj = {layers: $scope.layers, activeLayerIndex: $scope.activeLayerIndex}; 51 | $scope.$emit('layers', obj); 52 | console.log($scope.layers[$scope.activeLayerIndex]); 53 | }; 54 | }); 55 | -------------------------------------------------------------------------------- /assets/css/digitalslidearchive.css: -------------------------------------------------------------------------------- 1 | /*html and body need 100% as parent layers to allow child layers 100% height*/ 2 | html { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | height: 100%; 8 | padding-top: 50px; 9 | } 10 | 11 | /*This required to force the layer to 100% height*/ 12 | .full-height { 13 | height: 100%; 14 | overflow-y: hidden; 15 | } 16 | 17 | #image_viewer{ 18 | width:100%; 19 | 20 | /*http://stackoverflow.com/questions/2434602/css-setting-width-height-as-percentage-minus-pixels*/ 21 | /*for the height subtract the height of the toolbar*/ 22 | height: calc(100% - 50px); 23 | } 24 | 25 | #img_thumbnails{ 26 | padding-top:10px; 27 | } 28 | 29 | .top_buffer{ 30 | margin-top:20px; 31 | } 32 | 33 | .btn-primary img{ 34 | width: 20px; 35 | } 36 | 37 | .annot_delete_icon{ 38 | width:20px; 39 | } 40 | 41 | .panel-scroll { 42 | max-height: 400px; 43 | overflow: hidden; 44 | overflow-y: auto; 45 | } 46 | 47 | .image_viewer_toolbar 48 | { 49 | height: 33px; 50 | border: none; 51 | color: #333; 52 | padding: 4px; 53 | background-color: transparent; 54 | } 55 | 56 | .image_viewer_toolbar.fullpage{ 57 | width: 100%; 58 | border: none; 59 | position: fixed; 60 | z-index: 999999; 61 | left: 0; 62 | top: 0; 63 | background-color: #ccc; 64 | } 65 | 66 | /*Viewport properties window style*/ 67 | #viewport_properties_modal{ 68 | width:300px 69 | } 70 | 71 | #viewport_properties_modal .modal-dialog { 72 | height: 92%; 73 | width:300px; 74 | padding: 0; 75 | } 76 | 77 | #viewport_properties_modal .modal-content { 78 | height: 99%; 79 | } 80 | 81 | #viewport_properties_modal .modal-body { 82 | height: 90%; 83 | overflow-y: scroll; 84 | } 85 | 86 | /*Annotations window style*/ 87 | #annotations_modal{ 88 | width:500px; 89 | height: 600px; 90 | } 91 | 92 | #annotations_modal .modal-dialog { 93 | width:500px; 94 | padding: 0; 95 | } 96 | 97 | #annotations_modal .modal-body { 98 | height: 300px; 99 | overflow-y: scroll; 100 | } 101 | 102 | #annotations_modal .modal-body { 103 | height: 280px; 104 | } 105 | 106 | .active_layer { 107 | background-color:#EFEAEA; 108 | color:white; 109 | font-weight:bold; 110 | } 111 | -------------------------------------------------------------------------------- /app/components/image/controller.js: -------------------------------------------------------------------------------- 1 | //Define a controller for layers 2 | //Add root scope dependency 3 | app.controller("imageCtrl", function($rootScope, $scope, $window, $http){ 4 | $scope.activeImage = null; 5 | $scope.images = [ 6 | {'file_thumbnail': 'http://node15.cci.emory.edu/cgi-bin/iipsrv.fcgi?DeepZoom=/PYRAMIDS/PYRAMIDS/CDSA/GBM_Frozen/intgen.org_GBM.tissue_images.3.0.0/TCGA-06-0137-01A-01-BS1.svs.dzi.tif', 'filename_url': 'http://node15.cci.emory.edu/cgi-bin/iipsrv.fcgi?DeepZoom=/PYRAMIDS/PYRAMIDS/CDSA/GBM_Frozen/intgen.org_GBM.tissue_images.3.0.0/TCGA-06-0137-01A-01-BS1.svs.dzi.tif.dzi', 'id': 'TCGA-06-0137-01A-01-BS1'}, 7 | {"file_thumbnail": "http://node15.cci.emory.edu/cgi-bin/iipsrv.fcgi?DeepZoom=/PYRAMIDS/PYRAMIDS/CDSA/GBM_Frozen/intgen.org_GBM.tissue_images.3.0.0/TCGA-02-0034-01A-02-BS2.svs.dzi.tif", "prep_type": "FrozenSection", "pyramid_w_path": "/PYRAMIDS/PYRAMIDS/CDSA/GBM_Frozen/intgen.org_GBM.tissue_images.3.0.0/TCGA-02-0034-01A-02-BS2.svs.dzi.tif", "filename_url": "http://node15.cci.emory.edu/cgi-bin/iipsrv.fcgi?DeepZoom=/PYRAMIDS/PYRAMIDS/CDSA/GBM_Frozen/intgen.org_GBM.tissue_images.3.0.0/TCGA-02-0034-01A-02-BS2.svs.dzi.tif.dzi", "id": "TCGA-02-0034-01A-02-BS2","pyramid_base_name": "TCGA-02-0034-01A-02-BS2.svs.dzi.tif"}, 8 | {"file_thumbnail": "http://node15.cci.emory.edu/cgi-bin/iipsrv.fcgi?DeepZoom=/PYRAMIDS/PYRAMIDS/CDSA/GBM_Frozen/intgen.org_GBM.tissue_images.3.0.0/TCGA-02-0002-01A-01-BS1.svs.dzi.tif", "prep_type": "FrozenSection", "pyramid_w_path": "/PYRAMIDS/PYRAMIDS/CDSA/GBM_Frozen/intgen.org_GBM.tissue_images.3.0.0/TCGA-02-0002-01A-01-BS1.svs.dzi.tif", "filename_url": "http://node15.cci.emory.edu/cgi-bin/iipsrv.fcgi?DeepZoom=/PYRAMIDS/PYRAMIDS/CDSA/GBM_Frozen/intgen.org_GBM.tissue_images.3.0.0/TCGA-02-0002-01A-01-BS1.svs.dzi.tif.dzi", "id": "TCGA-02-0002-01A-01-BS1", "pyramid_base_name": "TCGA-02-0002-01A-01-BS1.svs.dzi.tif"}]; 9 | 10 | /** 11 | * Update the viewer when clicking on new image 12 | */ 13 | $scope.init = function(){ 14 | img = $scope.images[0]; 15 | $scope.activeImage = img; 16 | 17 | angular.element(document).ready(function () { 18 | $rootScope.$broadcast("activeImageId", img.id); 19 | }); 20 | } 21 | 22 | /** 23 | * Update the viewer when clicking on new image 24 | */ 25 | $scope.update = function(img){ 26 | //Set the active image 27 | $scope.activeImage = img; 28 | var viewer = $window.DSAViewer.getViewer(); 29 | viewer.open(img.filename_url); 30 | 31 | //Broadcast the active image ID 32 | $rootScope.$broadcast("activeImageId", img.id); 33 | } 34 | 35 | 36 | }); 37 | 38 | -------------------------------------------------------------------------------- /assets/js/colornames_to_hex_hash.js: -------------------------------------------------------------------------------- 1 | 2 | function colourNameToHex(colour) 3 | { 4 | var colours = {"aliceblue":"#f0f8ff","antiquewhite":"#faebd7","aqua":"#00ffff","aquamarine":"#7fffd4","azure":"#f0ffff", 5 | "beige":"#f5f5dc","bisque":"#ffe4c4","black":"#000000","blanchedalmond":"#ffebcd","blue":"#0000ff","blueviolet":"#8a2be2","brown":"#a52a2a","burlywood":"#deb887", 6 | "cadetblue":"#5f9ea0","chartreuse":"#7fff00","chocolate":"#d2691e","coral":"#ff7f50","cornflowerblue":"#6495ed","cornsilk":"#fff8dc","crimson":"#dc143c","cyan":"#00ffff", 7 | "darkblue":"#00008b","darkcyan":"#008b8b","darkgoldenrod":"#b8860b","darkgray":"#a9a9a9","darkgreen":"#006400","darkkhaki":"#bdb76b","darkmagenta":"#8b008b","darkolivegreen":"#556b2f", 8 | "darkorange":"#ff8c00","darkorchid":"#9932cc","darkred":"#8b0000","darksalmon":"#e9967a","darkseagreen":"#8fbc8f","darkslateblue":"#483d8b","darkslategray":"#2f4f4f","darkturquoise":"#00ced1", 9 | "darkviolet":"#9400d3","deeppink":"#ff1493","deepskyblue":"#00bfff","dimgray":"#696969","dodgerblue":"#1e90ff", 10 | "firebrick":"#b22222","floralwhite":"#fffaf0","forestgreen":"#228b22","fuchsia":"#ff00ff", 11 | "gainsboro":"#dcdcdc","ghostwhite":"#f8f8ff","gold":"#ffd700","goldenrod":"#daa520","gray":"#808080","green":"#008000","greenyellow":"#adff2f", 12 | "honeydew":"#f0fff0","hotpink":"#ff69b4", 13 | "indianred ":"#cd5c5c","indigo ":"#4b0082","ivory":"#fffff0","khaki":"#f0e68c", 14 | "lavender":"#e6e6fa","lavenderblush":"#fff0f5","lawngreen":"#7cfc00","lemonchiffon":"#fffacd","lightblue":"#add8e6","lightcoral":"#f08080","lightcyan":"#e0ffff","lightgoldenrodyellow":"#fafad2", 15 | "lightgrey":"#d3d3d3","lightgreen":"#90ee90","lightpink":"#ffb6c1","lightsalmon":"#ffa07a","lightseagreen":"#20b2aa","lightskyblue":"#87cefa","lightslategray":"#778899","lightsteelblue":"#b0c4de", 16 | "lightyellow":"#ffffe0","lime":"#00ff00","limegreen":"#32cd32","linen":"#faf0e6", 17 | "magenta":"#ff00ff","maroon":"#800000","mediumaquamarine":"#66cdaa","mediumblue":"#0000cd","mediumorchid":"#ba55d3","mediumpurple":"#9370d8","mediumseagreen":"#3cb371","mediumslateblue":"#7b68ee", 18 | "mediumspringgreen":"#00fa9a","mediumturquoise":"#48d1cc","mediumvioletred":"#c71585","midnightblue":"#191970","mintcream":"#f5fffa","mistyrose":"#ffe4e1","moccasin":"#ffe4b5", 19 | "navajowhite":"#ffdead","navy":"#000080", 20 | "oldlace":"#fdf5e6","olive":"#808000","olivedrab":"#6b8e23","orange":"#ffa500","orangered":"#ff4500","orchid":"#da70d6", 21 | "palegoldenrod":"#eee8aa","palegreen":"#98fb98","paleturquoise":"#afeeee","palevioletred":"#d87093","papayawhip":"#ffefd5","peachpuff":"#ffdab9","peru":"#cd853f","pink":"#ffc0cb","plum":"#dda0dd","powderblue":"#b0e0e6","purple":"#800080", 22 | "red":"#ff0000","rosybrown":"#bc8f8f","royalblue":"#4169e1", 23 | "saddlebrown":"#8b4513","salmon":"#fa8072","sandybrown":"#f4a460","seagreen":"#2e8b57","seashell":"#fff5ee","sienna":"#a0522d","silver":"#c0c0c0","skyblue":"#87ceeb","slateblue":"#6a5acd","slategray":"#708090","snow":"#fffafa","springgreen":"#00ff7f","steelblue":"#4682b4", 24 | "tan":"#d2b48c","teal":"#008080","thistle":"#d8bfd8","tomato":"#ff6347","turquoise":"#40e0d0", 25 | "violet":"#ee82ee", 26 | "wheat":"#f5deb3","white":"#ffffff","whitesmoke":"#f5f5f5", 27 | "yellow":"#ffff00","yellowgreen":"#9acd32"}; 28 | 29 | if (typeof colours[colour.toLowerCase()] != 'undefined') 30 | return colours[colour.toLowerCase()]; 31 | 32 | return false; 33 | } 34 | -------------------------------------------------------------------------------- /assets/js/mousetrap.min.js: -------------------------------------------------------------------------------- 1 | /* mousetrap v1.4.5 craig.is/killing/mice */ 2 | (function(J,r,f){function s(a,b,c){a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)}function A(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return h[a.which]?h[a.which]:B[a.which]?B[a.which]:String.fromCharCode(a.which).toLowerCase()}function t(a){a=a||{};var b=!1,c;for(c in n)a[c]?b=!0:n[c]=0;b||(u=!1)}function C(a,b,c,d,e,v){var g,k,f=[],h=c.type;if(!l[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(g=0;gg||h.hasOwnProperty(g)&&(p[h[g]]=g)}e=p[c]?"keydown":"keypress"}"keypress"==e&&f.length&&(e="keydown");return{key:d,modifiers:f,action:e}}function F(a,b,c,d,e){q[a+":"+c]=b;a=a.replace(/\s+/g," ");var f=a.split(" ");1":".","?":"/","|":"\\"},G={option:"alt",command:"meta","return":"enter",escape:"esc",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p,l={},q={},n={},D,z=!1,I=!1,u=!1;for(f=1;20>f;++f)h[111+f]="f"+f;for(f=0;9>=f;++f)h[f+96]=f;s(r,"keypress",y);s(r,"keydown",y);s(r,"keyup",y);var m={bind:function(a,b,c){a=a instanceof Array?a:[a];for(var d=0;d 0){ 112 | data[i] = $.extend({}, layer); 113 | data[i].markups = []; 114 | 115 | angular.forEach(layer.markups, function(markup, markupIndex){ 116 | data[i].markups.push(angular.copy(markup.data)); 117 | }); 118 | i++; 119 | } 120 | }); 121 | 122 | return data; 123 | }; 124 | 125 | /** 126 | * Clear all layers and markups 127 | **/ 128 | $scope.clear = function(){ 129 | $scope.layers = []; 130 | $scope.index = 0; 131 | $scope.options.name = 0; 132 | $scope.activeLayerIndex = 0; 133 | $window.annotationState.clearAnnotations(); 134 | $scope.activeLayer = null; 135 | } 136 | 137 | /** 138 | * Save the layers to a remote database 139 | */ 140 | $scope.save = function(){ 141 | var data = $scope.cleanup($scope.layers); 142 | 143 | if(data.length){ 144 | $http({ 145 | method: 'POST', 146 | url: api.url + ':' + api.port + '/annotations', 147 | data: data 148 | }); 149 | } 150 | }; 151 | 152 | /** 153 | * Load layers and markups from data source 154 | */ 155 | $scope.load = function(){ 156 | $http({ 157 | method: 'GET', 158 | url: api.url + ':' + api.port + '/annotations', 159 | params: {user_id: "Guest", image_id: $scope.options.imageId} 160 | }).then(function successCallback(response){ 161 | if(response.data.code == 404) return; 162 | 163 | var markups = []; 164 | angular.forEach(response.data.layers, function(layer, index){ 165 | angular.forEach(layer.markups, function(markup, junk){ 166 | markups.push(markup); 167 | }) 168 | }); 169 | 170 | $window.annotationState.loadAnnotations(markups); 171 | 172 | for(var i=0; i < response.data.layers.length; i++){ 173 | var layer = response.data.layers[i]; 174 | $scope.layers.push(layer); 175 | var markups = $.extend([], layer.markups); 176 | $scope.layers[i].markups = {}; 177 | 178 | for(var j=0; j < markups.length; j++){ 179 | var index = markups[j].index; 180 | var antIndex = markupService.getAnnotationIndex(index); 181 | $scope.layers[i].markups[index] = $window.annotationState.annotations[antIndex]; 182 | } 183 | } 184 | 185 | $scope.setActiveLayer(0); 186 | }, function errorCallback(response){ 187 | 188 | }); 189 | }; 190 | 191 | $scope.broadcastLayers = function(){ 192 | var obj = {layers: $scope.layers, activeLayerIndex: $scope.activeLayerIndex}; 193 | $scope.$broadcast('layers', obj); 194 | }; 195 | }); 196 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | openseadragon-annotations 2 | ========================= 3 | 4 | Openseadragon annotation is a web application that gives users the ability to specify deep zoom image source and then be able to markup and annotate the images. As of the now the application provides the following features: 5 | 6 | 1. Markup images using predefined shapes: rectangule, circle, polygon, free hand and a point. In addition, the user can select a color for each markup. 7 | 2. Add layers to which markups are assigned. The user can add/update/delete layers. 8 | 3. From the markup window, users can update the markup name, change color using a color picker and assign a layer. 9 | 10 | This branch only contains the client side application. Server side and web services are to be included in a different branch. 11 | 12 | Dependencies 13 | -------------------------- 14 | |Library| Version | Used for| 15 | |-------|---------|---------| 16 | |jQuery |2.1.4 | | 17 | |jQuery UI|1.11.4|Draggable modals| 18 | |Bootstrap|3.3.5|Layout and modals| 19 | |Knockout|3.0.0|| 20 | |Openseadragon|2.1|Key binding for the switch button| 21 | |Openseadragon scalebar||Add scalebar to the viewer| 22 | |Openseadragon imaginghelper|1.2|Fetch various viewer properties| 23 | |Openseadragon viewerinputhook|1.1.0|| 24 | |Bootstrap switch|3.3.2|Bootstrap on/off switch| 25 | |Bootstrap colorpicker||Markup color picker| 26 | |Angular JS|1.4.9|| 27 | 28 | 29 | 30 | This repo inludes only the client side application for the annotation. Parts of the code were written in Angular JS and Bootstrap and the directory structire is changed to AngularJS based structure. The Python web service was removed from this repo and will be push to a new standalone repo to keep things separated. 31 | 32 | This currently only works with a previous version of OpenSeaDragon 2.0. 33 | 34 | The UI needs some work. Currently, the UI contains three modals: 35 | 36 | 1. Markup drawing tools: you can select a shape and color 37 | 2. Markup list: to list all annotations, udpate the color using a color picker and add markup name 38 | 3. Layers list: a modal to add and update layers 39 | 4. Import Aperio XML file to DSA 40 | 5. Save markups and layers 41 | 6. Load markups and layers 42 | 43 | Within the OSD code, I generate an instance of my anntoator called 44 | 45 | annotationState() 46 | 47 | 48 | Once I drew some annotations I can do; 49 | currentdataset = annotationState.storeAnnotations() 50 | 51 | ##Various functionality 52 | To clear annotations you can call: 53 | annotationState.clearAnnotations() 54 | 55 | There's probably a cleaner way, but to save the data to the server, I did a 56 | 57 | myjson_as_a_string = JSON.stringy(currentdataset) 58 | 59 | This will which generate a string that I can push to the server; I can also reverse this oepration by doing 60 | 61 | JSON.parse(myjson_as_a_string) 62 | 63 | 64 | Once I clear the data, if I run 65 | 66 | annotationState.loadAnnotations(currentdataset) 67 | 68 | it will reload the ROI's I just drew. ROI's can also be drawn directly if I specify the JSON properly 69 | 70 | Attatched to this object is annotationState.annotations that actually stores the annotation data I just created. 71 | 72 | So after I draw a couple of objects, I issue the following from the javascript console: 73 | 74 | currentDataSet = annotationState.storeAnnotation() 75 | //This converts the individual annotations into a simpler JSON object for storing on the server. I also need to store additional metadata that actually tells me WHAT image the ROIs were drawn on 76 | 77 | Current working features 78 | --------------------------- 79 | 1. Add markup including circle, rectangular, polygon and a point 80 | 2. Update markup color, name and layer 81 | 3. Add/delete/update layers 82 | 83 | New features to work on 84 | --------------------------- 85 | 1. Load Aperio markups containing squares and circles 86 | 2. Fix the color conversion from Aperio to Hex color codes 87 | 88 | 89 | openseadragon-annotations 90 | ========================= 91 | This currently only works with a previous version of OpenSeaDragon 1.0.0; I will be upgrading this to work with the 2.0 version ASAP 92 | 93 | 94 | In your index.html you need to add the following two includes: 95 | 96 | 97 | 98 | 99 | 100 | jQuery is also a dependency and so is jQueryUI; In order to do annotation(s) you need to have some sort of widget/modal that pops up to allow the user to select the shape primitive 101 | 102 | This same functionality can also be used to load previously saved annotations back onto the OSD Canvas. 103 | 104 | 105 | So within the OSD code, I generate an instance of my anntoator called 106 | 107 | annotationState() 108 | 109 | 110 | Once I drew some annotations I can do; 111 | currentdataset = annotationState.storeAnnotations() 112 | 113 | ##Various functionality 114 | To clear annotations you can call: 115 | annotationState.clearAnnotations() 116 | 117 | There's probably a cleaner way, but to save the data to the server, I did a 118 | 119 | myjson_as_a_string = JSON.stringy(currentdataset) 120 | 121 | ## This will which generate a string that I can push to the server; I can also reverse this oepration by doing 122 | 123 | JSON.parse(myjson_as_a_string) 124 | 125 | 126 | # Once I clear the data, if I run 127 | 128 | annotationState.loadAnnotations(currentdataset) 129 | 130 | it will reload the ROI's I just drew. ROI's can also be drawn directly if I specify the JSON properly 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | Attatched to this object is annotationState.annotations that actually stores the annotation data I just created. 142 | 143 | So after I draw a couple of objects, I issue the following from the javascript console: 144 | 145 | currentDataSet = annotationState.storeAnnotation() 146 | //This converts the individual annotations into a simpler JSON object for storing on the server. I also need to store additional metadata that actually tells me WHAT image the ROIs were drawn on 147 | 148 | 149 | 150 | 151 | 152 | 153 | As a simple example, the following code will load four shapes onto the Canvas: 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /assets/libs/openseadragon-imaginghelper.min.js: -------------------------------------------------------------------------------- 1 | //! OpenSeadragonImagingHelper 1.2.0 2 | //! Build date: 2015-01-08 3 | //! Git commit: v1.2.0-0-g6ac5f20 4 | //! https://github.com/msalsbery/OpenSeadragonImagingHelper 5 | !function(a,b){function c(){this._haveImage=!0,this.imgWidth=this._viewer.viewport.contentSize.x,this.imgHeight=this._viewer.viewport.contentSize.y,this.imgAspectRatio=this.imgWidth/this.imgHeight,this._trackZoomPan()}function d(){this._haveImage=!1,this.imgWidth=0,this.imgHeight=0,this.imgAspectRatio=0}function e(){this._trackZoomPan()}function f(){this._trackZoomPan()}function g(){this._viewer&&this._viewer.autoResize&&this._trackZoomPan()}function h(){this._trackZoomPan()}function i(){this._trackZoomPan()}a.Viewer.prototype.activateImagingHelper=function(a){return this.imagingHelper||(a=a||{},a.viewer=this,this.imagingHelper=new b.ImagingHelper(a)),this.imagingHelper},b.ImagingHelper=function(b){if(b=b||{},!b.viewer)throw new Error("A viewer must be specified.");if(b.viewer.imagingHelper)throw new Error("Viewer already has an ImagingHelper.");this._viewer=b.viewer,a.EventSource.call(this),this._viewer.imagingHelper=this,this.options=b,this.imgWidth=0,this.imgHeight=0,this.imgAspectRatio=0,this._zoomFactor=1,this._minZoom=.001,this._maxZoom=10,this._zoomStepPercent=30,this._haveImage=!1,this._viewerSize=null,this._viewportWidth=0,this._viewportHeight=0,this._viewportOrigin=new OpenSeadragon.Point(0,0),this._viewportCenter=new OpenSeadragon.Point(0,0),b.onImageViewChanged&&this.addHandler("image-view-changed",b.onImageViewChanged),this._viewer.addHandler("open",a.delegate(this,c)),this._viewer.addHandler("close",a.delegate(this,d)),this._viewer.addHandler("animation",a.delegate(this,e)),this._viewer.addHandler("animation-finish",a.delegate(this,f)),this._viewer.addHandler("resize",a.delegate(this,g)),this._viewer.addHandler("full-page",a.delegate(this,h)),this._viewer.addHandler("full-screen",a.delegate(this,i))},b.ImagingHelper.version={versionStr:"1.2.0",major:1,minor:2,revision:0},a.extend(b.ImagingHelper.prototype,a.EventSource.prototype,{_raiseImageViewChanged:function(){this.raiseEvent("image-view-changed",{viewportWidth:this._viewportWidth,viewportHeight:this._viewportHeight,viewportOrigin:this._viewportOrigin,viewportCenter:this._viewportCenter,zoomFactor:this._zoomFactor})},_trackZoomPan:function(){var a=this._viewer.viewport.getBounds(!0);this._viewportOrigin.x=a.x,this._viewportOrigin.y=a.y*this.imgAspectRatio,this._viewportWidth=a.width,this._viewportHeight=a.height*this.imgAspectRatio,this._viewportCenter.x=this._viewportOrigin.x+this._viewportWidth/2,this._viewportCenter.y=this._viewportOrigin.y+this._viewportHeight/2,this._zoomFactor=this.getViewerContainerSize().x/(this._viewportWidth*this.imgWidth),this._raiseImageViewChanged()},getViewerContainerSize:function(){var b=this._viewer.container;return new a.Point(b.clientWidth,b.clientHeight)},notifyResize:function(){var a,b,c;this._haveImage&&(a=this.getViewerContainerSize(),a.equals(this._viewerSize)||(this._viewerSize=a,b=new OpenSeadragon.Point(this._viewportCenter.x,this._viewportCenter.y/this.imgAspectRatio),c=this._zoomFactor,this._viewer.viewport.resize(a,!1),this._viewer.viewport.zoomTo(c*this.imgWidth/a.x,null,!0),this._viewer.viewport.panTo(b,!0),this._raiseImageViewChanged()))},getMinZoom:function(){return this._minZoom},setMinZoom:function(a){this._minZoom=a,this._viewer.minZoomLevel=a*this.imgWidth/this.getViewerContainerSize().x},getMaxZoom:function(){return this._maxZoom},setMaxZoom:function(a){this._maxZoom=a,this._viewer.maxZoomLevel=a*this.imgWidth/this.getViewerContainerSize().x},getZoomStepPercent:function(){return this._zoomStepPercent},setZoomStepPercent:function(a){this._zoomStepPercent=a},setView:function(a,b,c,d){this._haveImage&&((this._viewportWidth!=a||this._viewportHeight!=b)&&this._viewer.viewport.zoomTo(1/a,null,d),(this._viewportCenter.x!=c.x||this._viewportCenter.y!=c.y)&&this._viewer.viewport.panTo(new OpenSeadragon.Point(c.x,c.y/this.imgAspectRatio),d))},getZoomFactor:function(){return this._zoomFactor},setZoomFactor:function(a,b){this._haveImage&&a!=this._zoomFactor&&a>0&&this._viewer.viewport.zoomTo(a*this.imgWidth/this.getViewerContainerSize().x,new OpenSeadragon.Point(this._viewportCenter.x,this._viewportCenter.y/this.imgAspectRatio),b)},zoomIn:function(a){var b=this._zoomFactor;b*=1+this._zoomStepPercent/100,b>this._maxZoom&&(b=this._maxZoom),this.setZoomFactor(b,a)},zoomOut:function(a){var b=this._zoomFactor;b/=1+this._zoomStepPercent/100,b0&&this._viewer.viewport.zoomTo(a*this.imgWidth/this.getViewerContainerSize().x,new OpenSeadragon.Point(b.x,b.y/this.imgAspectRatio),c)},zoomInAboutLogicalPoint:function(a,b){var c=this._zoomFactor;c*=1+this._zoomStepPercent/100,c>this._maxZoom&&(c=this._maxZoom),this.zoomAboutLogicalPoint(c,a,b)},zoomOutAboutLogicalPoint:function(a,b){var c=this._zoomFactor;c/=1+this._zoomStepPercent/100,c0?a/this.imgWidth:0},dataToLogicalY:function(a){return this._haveImage&&this.imgHeight>0?a/this.imgHeight:0},physicalToDataX:function(a){return this._haveImage&&this.getViewerContainerSize().x>0?(this._viewportOrigin.x+a/this.getViewerContainerSize().x*this._viewportWidth)*this.imgWidth:0},physicalToDataY:function(a){return this._haveImage&&this.getViewerContainerSize().y>0?(this._viewportOrigin.y+a/this.getViewerContainerSize().y*this._viewportHeight)*this.imgHeight:0},dataToPhysicalX:function(a){return this._haveImage&&this.imgWidth>0?(a/this.imgWidth-this._viewportOrigin.x)/this._viewportWidth*this.getViewerContainerSize().x:0},dataToPhysicalY:function(a){return this._haveImage&&this.imgHeight>0?(a/this.imgHeight-this._viewportOrigin.y)/this._viewportHeight*this.getViewerContainerSize().y:0}})}(OpenSeadragon,window.OpenSeadragonImaging=window.OpenSeadragonImaging||{}); -------------------------------------------------------------------------------- /assets/js/annotationState_control_functions.js: -------------------------------------------------------------------------------- 1 | /** Controller for the attribute controls window. */ 2 | function AnnotationStateControl(id) { 3 | this._init(id === undefined ? 'annotationControls' : id); 4 | } 5 | $.extend(AnnotationStateControl.prototype, { 6 | _init: function(id) { 7 | this.id = id; 8 | 9 | var annotationState = DERM.getAnnotationState(), 10 | $annotationState = $(annotationState); 11 | 12 | var self = this; 13 | $(function() { self.$el = $('#' + self.id); }); 14 | 15 | $annotationState.on('allAnnotationsChanged', $.proxy(this, 'refreshAnnotations')); 16 | $annotationState.on('annotationAdded', function(evt) { 17 | self.annotationAdded(evt.annotation, evt.index); 18 | }); 19 | }, 20 | 21 | refreshAnnotations: function() { 22 | var annotationState = DERM.getAnnotationState(), 23 | annotations = annotationState.annotations; 24 | $('#' + this.id).find("tr:gt(0)").remove(); 25 | for( var i = 0; i < annotations.length; ++i ) 26 | this.annotationAdded(annotations[i], i); 27 | }, 28 | 29 | annotationAdded: function(annotation, i) { 30 | if( typeof(i) === 'undefined' ) 31 | i = this.$el.find('tr:gt(0)').length; 32 | var html=''; 33 | html+=''+i+''; //could be changed to annotationState.annotations[i].data.id 34 | html+=''+annotation.data.type+''; 35 | html+=''+annotation.data.label+''; 36 | html+=''+'
'+''; 37 | html+=''+''; 38 | html+=''+annotation.data.filled+''; 39 | html+=''+''+''; 40 | html+=''; 41 | this.$el.append(html); 42 | 43 | /*need to add code for the toggle visibility here.... */ 44 | 45 | $(function() { 46 | $( '#'+i+'_alphaSlider' ).slider({ 47 | orientation: "horizontal", 48 | range: "min", 49 | max: 100, 50 | value: Math.round(annotation.data.alpha*100), 51 | change: function( event, ui ) { 52 | var roi_index=parseInt(this.id.split('_')[0]); 53 | var value=ui.value; 54 | var annotationState = DERM.getAnnotationState(); 55 | 56 | annotationState.updateAnnotationData(roi_index, {alpha: value/100.0}); 57 | } 58 | }); 59 | $( '#'+i+'_visibilityCheckbox' ).change(function(){ 60 | //console.log(this.id); 61 | var roi_index=parseInt(this.id.split('_')[0]); 62 | var annotationState = DERM.getAnnotationState(); 63 | if($(this).is(":checked")) { 64 | toggle_roi_properties( roi_index, 'visibility', false, annotationState) ; 65 | } else { 66 | toggle_roi_properties( roi_index, 'visibility', true, annotationState) ; 67 | } 68 | }); 69 | $( '#'+i+'_colorTextbox' ).on('keyup',function(){ 70 | var roi_index=parseInt(this.id.split('_')[0]); 71 | var new_color=this.value; 72 | if (new_color.length == 7 && new_color.charAt(0) == '#'){ 73 | DERM.getAnnotationState().updateAnnotationData(roi_index, {color: new_color}); 74 | } 75 | }); 76 | }); 77 | }, 78 | 79 | annotationRemoved: function(index) { 80 | this.$el.find('tr:eq(' + (index+1) + ')').remove(); 81 | }, 82 | }); 83 | 84 | 85 | function AnnotationStateToolbar(opts) { 86 | this._init(opts); 87 | } 88 | $.extend(AnnotationStateToolbar.prototype, { 89 | _DEFAULTS: { 90 | element: 'image_viewer_toolbar' 91 | }, 92 | 93 | _init: function(opts) { 94 | opts = $.extend({}, this._DEFAULTS, opts || {}); 95 | var element = opts.element; 96 | if( typeof element === 'string' || element instanceof String ) { 97 | element = document.getElementById(element.valueOf()); 98 | } 99 | this.element = element; 100 | this.annotationState = opts.annotationState; 101 | 102 | this._attachToControls(this.element); 103 | 104 | if( this.annotationState ) 105 | this.attach(this.annotationState); 106 | }, 107 | 108 | /** 109 | * Attaches listeners to all the controls. The controls are 110 | * stored in this.controls. 111 | */ 112 | _attachToControls: function() { 113 | var self = this; 114 | var $el = $(this.element); 115 | var controls = this.controls = { 116 | colorButtons: $el.find('#wsi_paint_color button'), 117 | currentColor: $el.find('#cur_color'), 118 | shapeButtons: $el.find('#wsi_active_shape button'), 119 | drawingSwitch: $el.find('.drawing_switch') 120 | }; 121 | 122 | controls.colorButtons.on('click', function (evt) { 123 | controls.colorButtons.removeClass('active'); 124 | $(this).addClass('active'); 125 | var hexColor = colourNameToHex(this.id); 126 | if (self.annotationState) { 127 | annotationState.lineColor = hexColor; 128 | } 129 | controls.currentColor.css('background-color', hexColor); 130 | }); 131 | 132 | 133 | controls.shapeButtons.on('click', function (evt) { 134 | controls.shapeButtons.removeClass('active'); 135 | $(this).addClass('active'); 136 | if (self.annotationState) { 137 | self.annotationState.setDrawMode(this.id); 138 | } 139 | }); 140 | 141 | controls.drawingSwitch.bootstrapSwitch(); 142 | controls.drawingSwitch.on('switch-change', function (e, data) { 143 | console.debug("switch-change caught by drawing switch element"); 144 | var $el = $(data.el), value = data.value; 145 | console.debug("switch change event is %o %O %O", e, $el, value); 146 | if (self.annotationState) { 147 | self.annotationState.setIsDrawing(!!data.value); 148 | } 149 | }); 150 | }, 151 | 152 | /** 153 | * Attaches to a particular annotation state. 154 | */ 155 | attachState: function(annotationState) { 156 | var self = this; 157 | this.annotationState = annotationState; 158 | var $state = $(annotationState); 159 | $state.on('isDrawingChanged', function (evt) { 160 | var drawingSwitch = self.controls.drawingSwitch; 161 | var isDrawing = evt.isDrawing; 162 | if( drawingSwitch.bootstrapSwitch('status') !== isDrawing ) { 163 | console.debug("annotation state caught isDrawingChanged event. Updating switch elements"); 164 | drawingSwitch.bootstrapSwitch('setState', isDrawing); 165 | } 166 | }); 167 | 168 | this.syncState(); 169 | }, 170 | 171 | setActiveColor: function (color) { 172 | this.controls.colorButtons.removeClass('active'); 173 | $(this.element).find('#wsi_paint_color #' + color).addClass('active'); 174 | }, 175 | 176 | /** 177 | * Synchronizes the toolbar controls with the current annotation state. 178 | */ 179 | syncState: function() { 180 | if (!this.annotationState) 181 | return; 182 | var state = this.annotationState, ctl = this.controls; 183 | ctl.drawingSwitch.bootstrapSwitch('setState', state.isDrawing); 184 | this.controls.currentColor.css('background-color', state.lineColor); 185 | this.controls.shapeButtons.removeClass('active'); 186 | $(this.element).find('#wsi_active_shape #' + state.drawMode).addClass('active'); 187 | }, 188 | }); 189 | 190 | /* toggling visibility in the annotation state object.. 191 | to get an individual object... it's annotationState.annotations[index] 192 | 193 | within that, the $element propery is what actually populates the div that is rendered... making changes to the visibilt of that will toggle things 194 | on and off.. but I need to make sure I propogate the event properly 195 | */ 196 | 197 | function toggle_roi_properties(roi_index, roi_property, prop_value, annotationState) { 198 | annotation_obj = annotationState.annotations[roi_index]; 199 | elmt_handle = annotation_obj.$element; 200 | 201 | if (roi_property == 'visibility') { 202 | prop_value ? $(elmt_handle).hide() : $(elmt_handle).show(); 203 | } 204 | } 205 | 206 | function get_url_for_poi_image(pin_color) { 207 | if (pin_color == 'FF0000' || pin_color == 'red') { 208 | pin_image_src = "drawing_icons/Pin1_Red.png"; 209 | } else if (pin_color == '00FF00' || pin_color == 'green') { 210 | pin_image_src = "drawing_icons/Pin1_Green.png"; 211 | } else { 212 | pin_image_src = "drawing_icons/Pin1_Blue.png"; 213 | } 214 | return (pin_image_src); 215 | } 216 | 217 | function annotation_setup_code(annotationState) { 218 | 219 | annotationToolbar = new AnnotationStateToolbar({ 220 | 221 | }); 222 | annotationToolbar.attachState(annotationState); 223 | 224 | 225 | Mousetrap.bind( ['ctrl+d'], function(evt) { 226 | if (typeof (evt.preventDefault) === 'function') 227 | evt.preventDefault(); 228 | else 229 | evt.returnValue = false; 230 | annotationState.setIsDrawing(!annotationState.isDrawing); 231 | }); 232 | 233 | return; 234 | } 235 | -------------------------------------------------------------------------------- /assets/js/bootstrap-switch.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrapSwitch v1.4 by Larentis Mattia @spiritualGuru 3 | * http://www.larentis.eu/switch/ 4 | * ============================================================ 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * ============================================================ */ 8 | 9 | !function ($) { 10 | "use strict"; 11 | 12 | $.fn['bootstrapSwitch'] = function (method) { 13 | var methods = { 14 | init: function () { 15 | return this.each(function () { 16 | var $element = $(this) 17 | , $div 18 | , $switchLeft 19 | , $switchRight 20 | , $label 21 | , myClasses = "" 22 | , classes = $element.attr('class') 23 | , color 24 | , moving 25 | , onLabel = "ON" 26 | , offLabel = "OFF" 27 | , icon = false; 28 | 29 | $.each(['switch-mini', 'switch-small', 'switch-large'], function (i, el) { 30 | if (classes.indexOf(el) >= 0) 31 | myClasses = el; 32 | }); 33 | 34 | $element.addClass('has-switch'); 35 | 36 | if ($element.data('on') !== undefined) 37 | color = "switch-" + $element.data('on'); 38 | 39 | if ($element.data('on-label') !== undefined) 40 | onLabel = $element.data('on-label'); 41 | 42 | if ($element.data('off-label') !== undefined) 43 | offLabel = $element.data('off-label'); 44 | 45 | if ($element.data('icon') !== undefined) 46 | icon = $element.data('icon'); 47 | 48 | $switchLeft = $('') 49 | .addClass("switch-left") 50 | .addClass(myClasses) 51 | .addClass(color) 52 | .html(onLabel); 53 | 54 | color = ''; 55 | if ($element.data('off') !== undefined) 56 | color = "switch-" + $element.data('off'); 57 | 58 | $switchRight = $('') 59 | .addClass("switch-right") 60 | .addClass(myClasses) 61 | .addClass(color) 62 | .html(offLabel); 63 | 64 | $label = $('